package online

import (
	"server/common"

	"github.com/mitchellh/mapstructure"
	"github.com/sirupsen/logrus"
)

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (s *OnlineService) validateNegotiateReq(msg common.Message, from common.ServiceInfo) (
	*common.P2PNegotiateReqData, *UserInfo, *common.AppError) {
	common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).WithFields(logrus.Fields{
		"from": from,
	}).Info("call validateNegotiateReq")

	var negotiateData common.P2PNegotiateReqData
	if err := mapstructure.Decode(msg.Data, &negotiateData); err != nil {
		errStr := "invalid negotiate request data format" + err.Error()
		common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).WithError(err).Error(errStr)
		return nil, nil, &common.AppError{ErrCode: 400, ErrMsg: errStr}
	}

	if negotiateData.CallId == "" {
		errStr := "callId is empty in negotiate request"
		common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).Error(errStr)
		return nil, nil, &common.AppError{ErrCode: 400, ErrMsg: errStr}
	}

	if negotiateData.UserId == "" {
		errStr := "userId is empty in negotiate request"
		common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).Error(errStr)
		return nil, nil, &common.AppError{ErrCode: 400, ErrMsg: errStr}
	}

	call, exists := s.callMgr.GetCall(negotiateData.CallId)
	if !exists {
		errStr := "call does not exist: " + negotiateData.CallId
		common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).Errorf(errStr)
		return nil, nil, &common.AppError{ErrCode: 404, ErrMsg: errStr}
	}

	// Call state must be Active for negotiation
	// if call.CallState != Active {
	// 	errStr := "call state is not Active, current state: " + CallStateToString(call.CallState)
	// 	common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).Errorf(errStr)
	// 	return nil, nil, &common.AppError{ErrCode: 400, ErrMsg: errStr}
	// }

	var remoteUserId string
	switch negotiateData.UserId {
	case call.CallerId:
		remoteUserId = call.CalleeId
	case call.CalleeId:
		remoteUserId = call.CallerId
	default:
		errStr := "userId does not match call participants: " +
			" caller " + call.CallerId + ", callee " + call.CalleeId
		common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).Error(errStr)
		return nil, nil, &common.AppError{ErrCode: 400, ErrMsg: errStr}
	}

	peerClient := s.userMgr.GetUserByUserId(remoteUserId)
	if peerClient == nil {
		errStr := "remote user not found: " + remoteUserId
		common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).Error(errStr)
		return nil, nil, &common.AppError{ErrCode: 404, ErrMsg: errStr}
	}

	return &negotiateData, peerClient, nil
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (s *OnlineService) handleNegotiateReq(msg common.Message, from common.ServiceInfo,
	connId string) error {
	common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).WithFields(logrus.Fields{
		"from": from,
	}).Info("call handleNegotiateReq")

	negotiateData, peerClient, appErr := s.validateNegotiateReq(msg, from)
	if appErr != nil {
		common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).WithError(appErr).
			Error("failed to validate negotiate request")
		responseMsg := common.Message{
			Cmd: common.P2P_NEGOTIATE_RES,
			Rid: msg.Rid,
			Data: common.ResMsgData{
				Code: appErr.ErrCode,
				Msg:  appErr.ErrMsg,
			},
		}
		if err := s.dispatcher.Dispatch(responseMsg, s.serviceInfo, from, connId); err != nil {
			common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).WithError(err).
				Error("failed to send negotiate response")
		}
	} else {
		if err := s.dispatcher.Dispatch(msg, s.serviceInfo, peerClient.ServiceInfo,
			peerClient.ConnId); err != nil {
			common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).WithError(err).
				Error("failed to dispatch negotiate request to peer client")
		} else {
			s.callMgr.updateCallState(negotiateData.CallId, Negotiating)
			common.Logger(common.SERVICE_TYPE_ONLINE, msg.Rid).WithFields(logrus.Fields{
				"callId": negotiateData.CallId,
				"userId": negotiateData.UserId,
				"from":   from,
			}).Info("dispatched negotiate request to peer client")
		}
	}

	return nil
}
